package com.vaadin.thomas.contenttool.ui;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.vaadin.hene.expandingtextarea.ExpandingTextArea;
import org.vaadin.tokenfield.TokenField;

import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.event.LayoutEvents.LayoutClickEvent;
import com.vaadin.event.LayoutEvents.LayoutClickListener;
import com.vaadin.event.MouseEvents;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.thomas.contenttool.data.Localization;
import com.vaadin.thomas.contenttool.data.Model;
import com.vaadin.thomas.contenttool.data.Tag;
import com.vaadin.thomas.contenttool.data.Value;
import com.vaadin.thomas.contenttool.ui.events.LangChangedEvent;
import com.vaadin.thomas.contenttool.ui.events.LangChangedListener;
import com.vaadin.thomas.contenttool.ui.events.TagsChangedEvent;
import com.vaadin.thomas.contenttool.ui.events.ValueAddedEvent;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.AbstractSelect.Filtering;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.DateField;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Layout;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.Window.CloseEvent;
import com.vaadin.ui.Window.CloseListener;

public class ExpandableValueComponent extends CssLayout implements
		LayoutClickListener, LangChangedListener {

	private static final long serialVersionUID = -3478345973273982751L;

	private HorizontalLayout header;
	private VerticalLayout content;

	private Value val;

	private boolean initialized = false;

	private Embedded exp;
	private ThemeResource expandedIcon = new ThemeResource(
			"icons/bullet_arrow_down.png");
	private ThemeResource collapsedIcon = new ThemeResource(
			"icons/bullet_arrow_right.png");

	public ExpandableValueComponent(Value value) {

		addStyleName("valuecomponent");
		setMargin(true, true, false, true);

		val = value;

		header = new HorizontalLayout();
		header.addListener(this);
		header.setWidth(100, UNITS_PERCENTAGE);
		header.setSpacing(true);
		header.addStyleName("header");
		addComponent(header);

		content = new VerticalLayout();
		content.setVisible(false);
		content.setWidth(100, UNITS_PERCENTAGE);
		content.addStyleName("content");
		content.setMargin(true, true, false, true);
		content.setSpacing(true);
		addComponent(content);

		refreshHeader();

		setWidth(100, UNITS_PERCENTAGE);
	}

	private void refreshHeader() {
		header.removeAllComponents();

		exp = new Embedded(null, collapsedIcon);
		exp.setSizeUndefined();
		exp.addStyleName("arrow");
		exp.addStyleName("icon");
		header.addComponent(exp);
		header.setComponentAlignment(exp, Alignment.MIDDLE_CENTER);

		Label identifier = new Label(val.getId());
		identifier.setSizeUndefined();
		header.addComponent(identifier);
		header.setExpandRatio(identifier, 1);

		Value valueFromPreview = Model.get().getPreview().getValuesInThis()
				.get(val.getId());
		if (valueFromPreview != null
				&& !valueFromPreview.getLocalizations().isEmpty()) {
			Embedded e = Util.getIcon("asterisk_orange");
			e.setSizeUndefined();
			header.addComponent(e);
			header.setComponentAlignment(e, Alignment.MIDDLE_CENTER);
		}

		for (Locale l : MainLayout.ct().conf().getAllowedLanguages()) {
			Embedded e = Util.getFlag(l.getLanguage());
			e.setSizeUndefined();
			header.addComponent(e);
			header.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

			if (!val.getAllLocalizations().containsKey(l)) {
				e.setEnabled(false);
			}
		}
	}

	private void refreshContent() {

		content.removeAllComponents();

		if (val.isDeleted()) {

			HorizontalLayout hl = new HorizontalLayout();
			hl.setSpacing(true);
			hl.setMargin(false, true, false, true);

			Label itemDeletedLabel = new Label(MainLayout.ct().get(
					"contenttool.content.item.isdeleted"));
			hl.addComponent(itemDeletedLabel);
			Button unDeleteButton = new Button(MainLayout.ct().get(
					"contenttool.content.item.undelete"));
			unDeleteButton.addListener(new UnDeleteListener());
			hl.addComponent(unDeleteButton);

			content.addComponent(hl);

		} else {

			for (final Locale loc : MainLayout.ct().conf()
					.getAllowedLanguages()) {

				List<Localization> locs = val.getAllLocalizations().get(loc);

				if (locs == null) {
					drawSingleLocalization(content, null, loc);
				} else {
					for (final Localization localization : locs) {
						drawSingleLocalization(content, localization, loc);
					}
				}
			}

			if (MainLayout.ct().conf().allowTags()) {
				TokenField tagsField = new TagField();
				tagsField.setValue(val.getTags());
				content.addComponent(tagsField);
			}
		}
	}

	private void drawSingleLocalization(Layout layout,
			final Localization localization, final Locale loc) {

		// GridLayout hl = new GridLayout(5, 1);
		HorizontalLayout hl = new HorizontalLayout();
		hl.setWidth(100, UNITS_PERCENTAGE);
		hl.setSpacing(true);

		Embedded e = Util.getFlag(loc.getLanguage());
		e.setSizeUndefined();
		hl.addComponent(e);
		hl.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

		final ExpandingTextArea value = new ExpandingTextArea();
		value.setWidth(100, UNITS_PERCENTAGE);
		value.setValue(localization == null ? "" : localization.getValue());
		value.addListener(new ValueChangeListener() {

			private static final long serialVersionUID = -986211935490113932L;

			@Override
			public void valueChange(ValueChangeEvent event) {

				Model.get().setValue(
						val.getId(),
						loc,
						value.getValue().toString(),
						localization == null ? null : localization
								.getStartDate());
				val = Model.get().getPreview().getValue(val.getId());
				refreshHeader();
				refreshContent();
			}
		});
		value.setImmediate(true);

		hl.addComponent(value);
		// hl.setColumnExpandRatio(1, 1);
		hl.setExpandRatio(value, 1);

		if (MainLayout.ct().conf().allowTimedPublishing()) {
			// not allowed for base localization
			if (localization != null && localization.getStartDate() != null) {

				DateField start = new DateField();
				start.setWidth(130, UNITS_PIXELS);
				start.setLocale(MainLayout.ct().getLang());
				start.setResolution(DateField.RESOLUTION_MIN);
				start.setValue(localization.getStartDate());
				start.addListener(new DateChangedListener(localization));
				hl.addComponent(start);
				hl.setComponentAlignment(start, Alignment.MIDDLE_CENTER);

			}

			// not allowed for null localization
			if (localization != null) {
				Embedded cal = Util.getIcon("calendar_add");
				cal.setDescription(MainLayout.ct().get(
						"contenttool.content.copy.copy_to_new"));
				hl.addComponent(cal);
				hl.setComponentAlignment(cal, Alignment.MIDDLE_CENTER);

				cal.addListener(new MouseEvents.ClickListener() {

					private static final long serialVersionUID = -9158135631343234642L;

					@Override
					public void click(
							com.vaadin.event.MouseEvents.ClickEvent event) {
						getWindow().addWindow(
								new AddLocalizationWithStartWindow(val,
										localization));
					}
				});
				cal.setImmediate(true);
			}
		}

		e = Util.getIcon("asterisk_yellow");
		e.setSizeUndefined();

		boolean valueInPreview = Model.get().getPreview().getValuesInThis()
				.containsKey(val.getId());
		boolean langInPreview = valueInPreview
				&& Model.get().getPreview().getValuesInThis().get(val.getId())
						.getLocalizations().containsKey(loc);
		e.setEnabled(langInPreview);

		hl.addComponent(e);
		hl.setComponentAlignment(e, Alignment.MIDDLE_CENTER);

		Button previous = new Button();
		previous.setCaption(MainLayout.ct().get(
				"contenttool.content.item.previous"));
		previous.addListener(new PreviousListener(loc));
		hl.addComponent(previous);
		hl.setComponentAlignment(previous, Alignment.MIDDLE_CENTER);

		Button clear = new Button();
		clear.addListener(new ClearListener(localization));
		clear.setCaption(MainLayout.ct().get("contenttool.content.item.clear"));
		clear.setEnabled(Model.get().isPartOfPreview(localization));
		hl.addComponent(clear);
		hl.setComponentAlignment(clear, Alignment.MIDDLE_CENTER);

		layout.addComponent(hl);
	}

	@Override
	public void layoutClick(LayoutClickEvent event) {
		setExpanded(!content.isVisible());
	}

	@Override
	public void langChanged(LangChangedEvent event) {
		refreshContent();
	}

	private Value getPossiblyNewValueForTags() {
		if (val.getVersion().isPreview()) {
			return val;
		}
		return Model.get().addValue(val.getId());
	}

	public void expand() {
		setExpanded(true);
	}

	public void collapse() {
		setExpanded(false);
	}

	private void setExpanded(boolean expanded) {
		if (!initialized) {
			refreshContent();
		}
		content.setVisible(expanded);
		if (expanded) {
			exp.setSource(expandedIcon);
		} else {
			exp.setSource(collapsedIcon);
		}
	}

	private class AddLocalizationWithStartWindow extends Window {

		private static final long serialVersionUID = 3539069012156340064L;

		public AddLocalizationWithStartWindow(final Value value,
				final Localization loc) {
			center();
			setWidth(40, UNITS_PERCENTAGE);
			setModal(true);
			((AbstractOrderedLayout) getContent()).setSpacing(true);
			getContent().setHeight(null);
			setHeight(null);

			setCaption(MainLayout.ct().get("contenttool.content.copy.msg"));

			final ExpandingTextArea valueField = new ExpandingTextArea();
			valueField.setValue(loc.getValue());
			valueField.setWidth(99, UNITS_PERCENTAGE);
			addComponent(valueField);

			final DateField startDate = new DateField();
			// startDate.setCaption(MainLayout.ct().get(
			// "contenttool.content.copy.start"));
			startDate.setLocale(MainLayout.ct().getLang());
			startDate.setResolution(DateField.RESOLUTION_MIN);
			startDate.setValue(loc.getStartDate() == null ? new Date() : loc
					.getStartDate());

			Button add = new Button();
			add.setCaption(MainLayout.ct().get("contenttool.content.copy.add"));
			add.addListener(new ClickListener() {

				private static final long serialVersionUID = 1314236768954523983L;

				@Override
				public void buttonClick(ClickEvent event) {
					getPossiblyNewValueForTags().addLocalization(
							Localization.create(loc.getLanguage(),
									(String) valueField.getValue(),
									(Date) startDate.getValue()));
					refreshHeader();
					refreshContent();
					close();
				}
			});

			HorizontalLayout hl = new HorizontalLayout();
			hl.setWidth(100, UNITS_PERCENTAGE);
			hl.setSpacing(true);
			hl.addComponent(startDate);
			hl.addComponent(add);
			hl.setComponentAlignment(startDate, Alignment.BOTTOM_RIGHT);
			hl.setComponentAlignment(add, Alignment.BOTTOM_LEFT);
			addComponent(hl);
		}
	}

	private final class TagField extends TokenField {

		private static final long serialVersionUID = -4803590607614234688L;

		public TagField() {
			setLayout(new HorizontalLayout());
			((AbstractOrderedLayout) getLayout()).setSpacing(true);
			setTokenInsertPosition(InsertPosition.AFTER);
			setCaption(MainLayout.ct().get(
					"contenttool.content.item.tags_caption"));
			setInputPrompt(MainLayout.ct().get(
					"contenttool.content.item.tags_hint"));
			setNewTokensAllowed(true);
			setFilteringMode(Filtering.FILTERINGMODE_CONTAINS);
			setContainerDataSource(new TagContainer());
			setTokenCaptionPropertyId(TagContainer.CAPTION_PROPID);
			setRememberNewTokens(false);
		}

		@Override
		protected void onTokenInput(Object tokenId) {

			Tag t;
			if (tokenId instanceof Tag) {
				t = (Tag) tokenId;
			} else {
				t = Tag.get(tokenId.toString());
			}
			if (getContainerDataSource().getItem(t) == null) {
				getContainerDataSource().addItem(t);
				getContainerDataSource().getItem(t)
						.getItemProperty(TagContainer.CAPTION_PROPID)
						.setValue(t.getValue());
			}

			@SuppressWarnings("unchecked")
			Set<Object> set = (Set<Object>) getValue();
			if (set != null && set.contains(t)) {
				// duplicate
			} else {
				addToken(t);
			}
			getPossiblyNewValueForTags().addTag(t);
			MainLayout.bb().fire(new TagsChangedEvent());
		}

		// show confirm dialog
		@Override
		protected void onTokenClick(final Object tokenId) {

			Tag t;
			if (tokenId instanceof Tag) {
				t = (Tag) tokenId;
			} else {
				t = Tag.get((String) tokenId);
			}
			removeToken(t);
			getPossiblyNewValueForTags().removeTag(t);
			MainLayout.bb().fire(new TagsChangedEvent());
		}
	}

	private class ClearListener implements ClickListener {

		private static final long serialVersionUID = -2025814946676092853L;

		final Localization localization;

		public ClearListener(Localization localization) {
			this.localization = localization;
		}

		@Override
		public void buttonClick(ClickEvent event) {
			Model.get().discardLocalization(localization);
			refreshHeader();
			refreshContent();
		}
	}

	private class UnDeleteListener implements ClickListener {

		private static final long serialVersionUID = -4533917662267828286L;

		@Override
		public void buttonClick(ClickEvent event) {
			val.unDelete();
			refreshContent();
			MainLayout.bb().fire(new ValueAddedEvent(val));
		}
	}

	private class PreviousListener implements ClickListener {

		private static final long serialVersionUID = -4533917662267828286L;

		final Locale localization;

		public PreviousListener(Locale localization) {
			this.localization = localization;
		}

		@Override
		public void buttonClick(ClickEvent event) {
			Window w = new PreviousValuesWindow(val, localization);
			getWindow().addWindow(w);

			w.addListener(new CloseListener() {

				private static final long serialVersionUID = 8465663960085535763L;

				@Override
				public void windowClose(CloseEvent e) {
					refreshHeader();
					refreshContent();
				}
			});
		}
	}

	private class DateChangedListener implements ValueChangeListener {

		private static final long serialVersionUID = 7950421796737822218L;

		private Localization l;

		public DateChangedListener(Localization l) {
			this.l = l;
		}

		@Override
		public void valueChange(ValueChangeEvent event) {
			Date newDate = (Date) event.getProperty().getValue();
			l.setStartDate(newDate);
		}

	}
}
